#!/usr/bin/env python
# coding=UTF-8
import random
import logging
import json
import time
import urllib
import datetime
import itertools
from threading import Timer

import redisco
from redisco.containers import List, Hash


from tools import nsq_pub, json_encode
import config
import dao

def get_last_data(bid, uid, rid):
    all_player_data = []
    dn = DouNiu(rid, bid)
    db = dao.Game_DB()
    for member in dn.members:
        p = Player(member, bid, rid)
        player_data = p.get_data()
        player_info = db.get_player(rid, uid)
        player_data['status'] = player_info['status']
        player_data['user_score'] = player_info['score']
        all_player_data.append(player_data)
    game_round = dn.room['game_round']
    all_round = dn.room['all_round']
    dealer = dn.room['dealer']
    status = dn.room['status']
    pub_data = {"act":"get_last_data",
                "game_round":game_round, 
                "all_round":all_round, 
                "dealer":dealer,
                "status":status,
                "rid":rid,
                "all_player_data":all_player_data,
                }
    nsq_pub({"b":bid, "u":uid, "t":"game", "d":pub_data})
    tips_user = dn.tips_user
    if tips_user:
        if uid in tips_user:
            p = Player(uid, bid, rid)
            stop_pop_act = dn.room['stop_pop_act']
            nsq_pub({"b":bid, "u":uid, "t":"game", "d":{"act":stop_pop_act, "uid":uid, "rid":rid}})
        
class Player():
    def __init__(self, uid, bid='', rid='', first=False):
        self.uid = uid
        self.bid = bid
        self.rid = rid
        if first:
            redisco.connection.delete("p:%s:%s:%s:current_card" % (bid, uid, rid))
            redisco.connection.delete("p:%s:%s:%s" % (bid, uid, rid))
            self.pinfo = Hash("p:%s:%s:%s" % (bid, uid, rid))
            self.pinfo['score'] = 0
            self.pinfo['double_value'] = 1
            self.pinfo['card_type'] = ''
            self.pinfo['max_value'] = ''
            self.pinfo['card_type_name'] = ''
            self.pinfo['format_card'] = ''
        else:
            self.pinfo = Hash("p:%s:%s:%s" % (bid, uid, rid))
        self.current_card = List("p:%s:%s:%s:current_card" % (bid, uid, rid))
    
    def clear(self):
        redisco.connection.delete("p:%s:%s:%s:current_card" % (self.bid, self.uid, self.rid))
        redisco.connection.delete("p:%s:%s:%s" % (self.bid, self.uid, self.rid))
        
    def get_data(self):
        return {"uid":self.uid,
                "current_card":self.current_card.members,
                "score":self.pinfo['score'],
                "double_value":self.pinfo['double_value'],
                "card_type":self.pinfo['card_type'],
                #"max_value":self.pinfo['max_value'],
                #"card_type_name":self.pinfo['card_type_name'],
                #"format_card":self.pinfo['format_card'],
                }
        
class DouNiu():
    def __init__(self, rid, bid, dealer=None):
        self.rid = str(rid)
        if dealer:
            redisco.connection.delete("r:%s:current_card" % rid)
            redisco.connection.delete("r:%s:mid" % rid)
            redisco.connection.delete("r:%s:tips_user" % rid)
            redisco.connection.delete("r:%s:rob_user" % rid)
            self.room = Hash("r:%s" % rid)
            self.room['dealer'] = dealer
            self.room['stop_pop_act'] = ''
        else:
            self.room = Hash("r:%s" % rid)
        self.current_card = List("r:%s:current_card" % rid)
        self.bid = bid
        l = List("r:%s:p" % rid)
        self.members = l.members
        self.tips_user = List("r:%s:tips_user" % rid)
        self.rob_user = List("r:%s:rob_user" % rid)
    
    def clear_room(self):
        redisco.connection.delete("r:%s:current_card" % self.rid)
        redisco.connection.delete("r:%s:mid" % self.rid)
        redisco.connection.delete("r:%s:tips_user" % self.rid)
        redisco.connection.delete("r:%s:rob_user" % self.rid)
        redisco.connection.delete("r:%s" % self.rid)
            
    def incr_mid(self):
        return redisco.connection.incr("r:%s:mid" % self.rid)
    
    def get_card_point(self, c):
        '''获取某张扑克牌的点数'''
        d = int(c)/10
        if d > 10:
            d = 10
        return d
    
    def trans_card(self, card):
        '''转换手牌列表,去掉花色'''
        res = []
        card.sort()
        for c in card:
            card_point = self.get_card_point(c)
            res.append(card_point)
        return res
    
    def trans_int_card(self, card):
        res = [int(x) for x in card]
        return res
    
    def get_double_value(self, card_type, mode='classic'):
        if mode == 'classic':
            value_conf = {"color_seq":5,
                      "bomb":5,
                      "five_flower":5,
                      "five_small":5,
                      "same_color":5,
                      "seq":5,
                      "bull_10":3,
                      "bull_9":2,
                      "bull_8":2,
                      "bull_7":2,
                      "bull_6":1,
                      "bull_5":1,
                      "bull_4":1,
                      "bull_3":1,
                      "bull_2":1,
                      "bull_1":1,
                      "bull_0":1,
                      }
        else:
            value_conf = {"color_seq":25,
                      "bomb":20,
                      "five_flower":20,
                      "five_small":20,
                      "same_color":20,
                      "seq":20,
                      "bull_10":10,
                      "bull_9":10,
                      "bull_8":10,
                      "bull_7":10,
                      "bull_6":10,
                      "bull_5":10,
                      "bull_4":10,
                      "bull_3":10,
                      "bull_2":10,
                      "bull_1":1,
                      "bull_0":1,
                      }
        return value_conf[card_type]
    
    def get_bull(self, all_card):
        n = 0
        max_value = max([int(x) for x in all_card])
        for group in itertools.combinations(all_card, 3):
            group = list(group)
            group_card = self.trans_card(group)
            if sum(group_card) % 10 == 0:
                left_group = list(set(all_card)-set(group))
                left_card = self.trans_card(left_group)
                n = sum(left_card) % 10
                if n == 0:
                    n = 10
                res = []
                group.sort(key=lambda x:int(x))
                res.extend(group)
                left_group.sort(key=lambda x:int(x))
                res.extend(left_group)
                return 'bull_%s' % n, [res, max_value]
        return 'bull_0', [all_card, max_value]
    
    def is_color_seq(self, all_card):
        seq = self.is_seq(all_card)
        if seq[0]:
            same_color = self.is_same_color(all_card)
            if same_color[0]:
                return 'color_seq', same_color[-1]
        return False, 0
    
    def is_bomb(self, all_card):
        card = self.trans_card(all_card)
        for c in set(card):
            if card.count(c) == 4:
                return 'bomb', int(c)
        return False, 0
    
    def is_five_flower(self, all_card):
        res = []
        for card in all_card:
            if int(card) < 110:
                return False, 0
            else:
                res.append(int(card))
        return 'five_flower', max(res)
    
    def is_five_small(self, all_card):
        card = []
        for c in all_card:
            d = int(c) / 10
            if d > 5:
                return False, 0
            card.append(d)
        if sum(card) <= 10:
            all_card = self.trans_int_card(all_card)
            return 'five_small', max(all_card)
        return False, 0
    
    def is_triple_pair(self, all_card):
        tiple_list = []
        pair_list = []
        card = self.trans_card(all_card)
        for c in set(card):
            n = card.count(c)
            if n == 3:
                tiple_list.append(c)
            elif n == 2:
                pair_list.append(c)
            else:
                return False, 0
        if len(tiple_list) == 1 and len(pair_list) == 1:
            return 'triple_pair', int(tiple_list[0])
        return False, 0
    
    def is_same_color(self, all_card):
        color = int(all_card[0]) % 10
        card = []
        for c in all_card:
            if int(c) % 10 != color:
                return False, 0
            else:
                card.append(int(c))
        return 'same_color', max(card)
    
    def is_seq(self, all_card):
        card = self.trans_card(all_card)
        card.sort()
        c_s = card[0]
        c_e = card[-1]
        if card == range(c_s, c_e+1):
            return 'seq', c_e
        return False, 0
    
    def get_card_type(self, all_card):
        is_color_seq = self.is_color_seq(all_card)
        if is_color_seq[0]:
            return is_color_seq
        is_bomb = self.is_bomb(all_card)
        if is_bomb[0]:
            return is_bomb
        is_five_flower = self.is_five_flower(all_card)
        if is_five_flower[0]:
            return is_five_flower
        is_five_small = self.is_five_small(all_card)
        if is_five_small[0]:
            return is_five_small 
        is_same_color = self.is_same_color(all_card)
        if is_same_color[0]:
            return is_same_color
        is_seq = self.is_seq(all_card)
        if is_seq[0]:
            return is_seq
        return self.get_bull(all_card)
    
    def compare_card(self, card1, card2):
        #判断card2是否比card1牌型大
        card_type_conf = ['bull_0', 'bull_1', 'bull_2','bull_3','bull_4','bull_5','bull_6','bull_7','bull_8','bull_9','bull_10',
                          'seq','same_color','triple_pair', 'five_small', 'five_flower', 'bomb', 'color_seq']
        card_type2, max_res2 = self.get_card_type(card2)
        card_type1, max_res1 = self.get_card_type(card1)
        if card_type1 == card_type2:
            #相同类型的牌
            if 'bull' in card_type1:
                max_value1 = max_res1[-1]
                max_value2 = max_res2[-1]
                if max_value2 > max_value1:
                    return True
                return False
            else:
                if max_res2 > max_res1:
                    return True
                return False
        c2_idx = card_type_conf[card_type2]
        c1_idx = card_type_conf[card_type1]
        if c2_idx > c1_idx:
            return True
        return False
    
    def start(self):
        self.room['status'] = 'start'
        game_round = int(self.room['game_round'])
        all_card = []
        for i in range(10, 140, 10):
            for j in range(1, 5):
                all_card.append(str(i+j))
        
        random.shuffle(all_card)
        
        dealer = self.room['dealer']
        user_card = {}
        player_card = []
        for uid in self.members:
            card = []
            for i in range(4):
                card.append(all_card.pop())
            card.sort()
            card.reverse()
            user_card[uid] = card
            player_card.append({"uid":uid, "current_card":card})
            
        for uid in self.members:
            mid = self.incr_mid()
            player = Player(uid, self.bid, self.rid, True)
            card = user_card[uid]
            player.current_card.extend(card)
            card_list = [str(x) for x in card]
            nsq_pub({"b":self.bid, "u":uid, "t":"game", "d":{"act":"game_start", "status":"start", "rid":self.rid, "mid":mid, "card":card_list, "game_round":self.room['game_round'], "all_round":self.room['all_round'], "player_card":player_card}})
        self.current_card.clear()
        self.current_card.extend(all_card)
        
        logging.info(u"斗牛房间rid:%s,game_round:%s,发牌结束" % (self.rid, self.room['game_round']))
        
        dealer_mode = self.room['dealer_mode']
        
        if dealer_mode == 'rob':
            self.notify_turn_rob()
        else:
            self.pub_rob(dealer)
            
    def notify_turn_rob(self):
        #通知玩家抢庄
        self.room['stop_pop_act'] = 'turn_rob'
        self.tips_user.extend(self.members)
        for member in self.members:
            nsq_pub({"b":self.bid, "u":member, "t":"game", "d":{"act":"turn_rob", "uid":member, "rid":self.rid}})
    
        timer = Timer(5, self.auto_cancel_rob)
        timer.start()
    
    def auto_cancel_rob(self):
        stop_pop_act = self.room['stop_pop_act']
        if stop_pop_act == 'turn_rob':
            tips_user = self.tips_user.members
            for uid in tips_user:
                self.cancel_rob(uid, {})
                    
    def cancel_rob(self, uid, data={}):
        #不抢
        if uid not in self.tips_user:
            return
        self.tips_user.remove(uid)
        for member in self.members:
            nsq_pub({"b":self.bid, "u":member, "t":"game", "d":{"act":"cancel_rob", "uid":uid, "rid":self.rid}})
            
        if not self.tips_user:
            self.room['stop_pop_act'] = ''
            self.pub_rob()
            
    def rob(self, uid, data={}):
        if uid not in self.tips_user:
            return
        self.tips_user.remove(uid)
        for member in self.members:
            nsq_pub({"b":self.bid, "u":member, "t":"game", "d":{"act":"rob", "uid":uid, "rid":self.rid}})
            
        self.rob_user.append(uid)
        if not self.tips_user:
            self.room['stop_pop_act'] = ''
            self.pub_rob()
    
    def pub_rob(self, dealer=None):
        
        if not dealer:
            rob_user = self.rob_user.members
            if rob_user:
                dealer = random.choice(rob_user)
            else:
                dealer = random.choice(self.members)
        else:
            rob_user = []
        if len(rob_user) == 1:
            rob_user = []
        self.room['dealer'] = dealer
        for member in self.members:
            nsq_pub({"b":self.bid, "u":member, "t":"game", "d":{"act":"pub_rob", "dealer":dealer, "rid":self.rid, "rob_user":rob_user}})
        
        timer = Timer(2, self.turn_double_select, (dealer, ))
        timer.start()
    
    def turn_double_select(self, dealer):
        self.room['stop_pop_act'] = 'turn_double_select'
        
        for member in self.members:
            if member != dealer:
                self.tips_user.append(member)
                nsq_pub({"b":self.bid, "u":member, "t":"game", "d":{"act":"turn_double_select", "uid":member, "rid":self.rid}})
        
        timer = Timer(5, self.auto_double_select)
        timer.start()
    
    def auto_double_select(self):
        stop_pop_act = self.room['stop_pop_act']
        if stop_pop_act == 'turn_double_select':
            tips_user = self.tips_user.members
            for uid in tips_user:
                self.double_select(uid, {"double_value":1})
        
    def double_select(self, uid, data):
        #选择加倍
        if uid not in self.tips_user:
            return
        self.tips_user.remove(uid)
        player = Player(uid, self.bid, self.rid)
        double_value = data['double_value']
        player.pinfo['double_value'] = double_value
        for member in self.members:
            nsq_pub({"b":self.bid, "u":member, "t":"game", "d":{"act":"double_select", "uid":uid, "double_value":double_value, "rid":self.rid}})
        
        if not self.tips_user:
            self.room['stop_pop_act'] = ''
            self.next_pop()
    
    def next_pop(self):
        dealer = self.room['dealer']
        members = self.members
        player_cnt = len(members)
        dealer_seat = members.index(dealer)
        #player_card = []
        for i in range(player_cnt):
            idx = (dealer_seat + i) % player_cnt
            member = members[idx]
            member_player = Player(member, self.bid, self.rid)
            card = self.current_card.pop()
            member_player.current_card.append(card)
            #player_card.append({"uid":member, "card":card, "current_card":member_player.current_card.members})
            nsq_pub({"b":self.bid, "u":member, "t":"game", "d":{"act":"get_card", "uid":member, "card":card, "rid":self.rid}})
        
        timer = Timer(2, self.show_res)
        timer.start()
        
    def show_res(self):
        dealer = self.room['dealer']
        members = self.members
        player_cnt = len(members)
        dealer_seat = members.index(dealer)
        dealer_player = Player(dealer, self.bid, self.rid) 
        dealer_card = dealer_player.current_card.members
        dealer_card_type, max_res = self.get_card_type(dealer_card)
        if 'bull' in dealer_card_type and 'bull_0' != dealer_card_type:
            dealer_format_card = max_res[0] 
            dealer_max_value = max_res[-1]
        else:
            dealer_format_card = dealer_card
            dealer_max_value = max_res
        dealer_player.pinfo['card_type'] = dealer_card_type
        dealer_player.pinfo['max_value'] = dealer_max_value
        dealer_player.pinfo['format_card'] = ",".join(dealer_format_card)
        dealer_mode = self.room['dealer_mode']
        if dealer_mode == 'rob':
            next_dealer = ''
        elif dealer_mode == 'turn' and dealer_card_type == 'bull_0':
            idx = (dealer_seat + 1) % player_cnt
            next_dealer = members[idx]
        else:
            next_dealer = dealer
        for i in range(1, player_cnt):
            idx = (dealer_seat + i) % player_cnt
            member = members[idx]
            member_player = Player(member, self.bid, self.rid)
            current_card = member_player.current_card.members
            card_type, max_res = self.get_card_type(current_card)
            if 'bull' in card_type and 'bull_0' != card_type:
                format_card = max_res[0] 
                max_value = max_res[-1]
            else:
                format_card = current_card
                max_value = max_res
            member_player.pinfo['card_type'] = card_type
            member_player.pinfo['max_value'] = max_value
            member_player.pinfo['format_card'] = ",".join(format_card)
            for mm in members:
                nsq_pub({"b":self.bid, "u":mm, "t":"game", "d":{"act":"show_res", "uid":member, "card":format_card, "card_type":card_type, "rid":self.rid}})
            time.sleep(0.5)
            
        for mm in members:
            nsq_pub({"b":self.bid, "u":mm, "t":"game", "d":{"act":"show_res", "uid":dealer, "card":dealer_format_card, "card_type":dealer_card_type, "rid":self.rid}})
        
        timer = Timer(1, self.game_over, (next_dealer,))
        timer.start()
        
    def game_over(self, next_dealer='', from_dissmiss=False):
        dealer = self.room['dealer']
        members = self.members
        player_cnt = len(members)
        dealer_seat = members.index(dealer)
        dealer_player = Player(dealer, self.bid, self.rid)
        dealer_card = dealer_player.current_card.members
        db = dao.Game_DB()
        for i in range(1, player_cnt):
            idx = (dealer_seat + i) % player_cnt
            member = members[idx]
            member_player = Player(member, self.bid, self.rid)
            member_card = member_player.current_card.members
            double_value = int(member_player.pinfo['double_value'])
            compare_res = self.compare_card(dealer_card, member_card)
            if compare_res:
                card_type = member_player.pinfo['card_type']
                card_double = self.get_double_value(card_type)
                win_score = card_double*double_value
                member_player.pinfo['score'] = win_score
                db.update_player(self.rid, member, win_score, 0)
                dealer_player.pinfo['score'] = int(dealer_player.pinfo['score']) - win_score
                db.update_player(self.rid, dealer, -win_score, 0)
            else:
                card_type = dealer_player.pinfo['card_type']
                card_double = self.get_double_value(card_type)
                win_score = card_double*double_value
                member_player.pinfo['score'] = -win_score
                db.update_player(self.rid, member, -win_score, 0)
                dealer_player.pinfo['score'] = int(dealer_player.pinfo['score']) + win_score
                db.update_player(self.rid, dealer, win_score, 0)
            db.commit()
        
        game_round = self.room['game_round']
        if not game_round:
            game_round = 1
        else:
            game_round = int(game_round)
        player_card = []
        card_type_conf = {"color_seq":u"同花顺",
                          "bomb":u"炸弹",
                          "five_flower":u"五花牛",
                          "five_small":u"五小牛",
                          "triple_pair":u"葫芦",
                          "same_color":u"同花",
                          "seq":u"顺子",
                          "bull_10":u"牛牛",
                          "bull_9":u"牛9",
                          "bull_8":u"牛8",
                          "bull_7":u"牛7",
                          "bull_6":u"牛6",
                          "bull_5":u"牛5",
                          "bull_4":u"牛4",
                          "bull_3":u"牛3",
                          "bull_2":u"牛2",
                          "bull_1":u"牛1",
                          "bull_0":u"没牛",
                          }
        for i in range(1, player_cnt+1):
            idx = (dealer_seat + i) % player_cnt
            member = members[idx]
            player_info = db.get_player(self.rid, member)
            member_player = Player(member, self.bid, self.rid)
            user_card = member_player.pinfo['format_card'].split(",")
            if member == dealer:
                is_dealer = 'y'
            else:
                is_dealer = 'n'
            win_score = member_player.pinfo['score']
            card_type = member_player.pinfo['card_type']
            card_type_name = card_type_conf[card_type]
            user_data= {"is_dealer":is_dealer, "card_type":card_type, "card_type_name":card_type_name,"user_card":user_card, "win_score":win_score, "user_score":player_info['score'], "uid":member}
            player_card.append(user_data)
            bull_8 = 0
            bull_9 = 0
            bull_10 = 0
            bomb = 0
            five_flower = 0
            five_small = 0
            
            db.add_dn_room_log(0, 0, self.rid, game_round, member, win_score, bull_8, bull_9, bull_10, bomb, five_flower, five_small, is_dealer)
            db.commit()
            
        room_over = 'n'
        if from_dissmiss:
            room_over = 'y'
        
        db.game_over(self.rid, game_round)
        db.commit()
        mid = self.incr_mid()
        game_type = 'dn'
        self.room['dealer'] = next_dealer
        self.room['game_round'] = game_round + 1
        for member in self.members:
            pub_data = {"b":self.bid, "u":member, "t":"game", "d":{"mid":mid, "act":"game_over", "room_over":room_over, "game_type":game_type, "dealer":dealer, "player_card":player_card, "game_round":game_round, "rid":self.rid,}}
            nsq_pub(pub_data)
            #db.save_game_over(self.rid, game_round, "game_over", member, urllib.quote(json_encode(pub_data).encode("utf-8")))
            #db.commit()
            #try:
            #    member_player = Player(member, self.bid, self.rid)
            #    if int(member_player.pinfo['score']) > 0:
            #        is_win = 1
            #    else:
            #        is_win = 0
            #    if db.has_user_stat(self.bid, member, game_type):
            #        db.update_user_stat(self.bid, member, game_type, is_win)
            #    else:
            #        db.add_user_stat(self.bid, member, game_type, is_win, 0)
            #except:
            #    logging.error(u"斗牛统计生涯错误:%s" % member)
                
        if room_over == 'y':
            self.dismiss()
    
    def dismiss(self):
        db = dao.Game_DB()
        player_data = {}
        player_stat = []
        
        p_stat = db.player_stat_dn(self.rid)
        for ps in p_stat:
            ps['bull_8'] = int(ps['bull_8'])
            ps['bull_9'] = int(ps['bull_9'])
            ps['bull_10'] = int(ps['bull_10'])
            ps['bomb'] = int(ps['bomb'])
            ps['five_small'] = int(ps['five_small'])
            ps['five_flower'] = int(ps['five_flower'])
            ps['score'] = int(ps['score'])
            player_data[str(ps['uid'])] = ps
        
        if not p_stat:
            deduct_flag = False
            winner = ""
            for uid in self.members:
                player_stat.append({"uid":uid,"bull_8":0,"bull_9":0,"bull_10":0,"bomb":0,"five_small":0,"five_flower":0,"score":0})
        else:
            player_stat = p_stat
            winner = p_stat[-1]['uid']
            deduct_flag = True
            
        room_info = db.get_room(self.rid, self.bid)
        admin = room_info['admin']
        match_id = room_info['mid']
        gid = room_info['gid']
        if not gid:
            gid = 0
        for ps in player_stat:
            if ps['uid'] == winner:
                is_winner = 'y'
            else:
                is_winner = 'n'
            db.add_room_stat(match_id, gid, self.rid, ps['uid'], ps['score'], is_winner)
            db.commit()
            
        for uid in self.members:
            if not uid:
                continue
            pub_data = {"b":self.bid, "u":str(uid), "t":"game", "d":{"act":"room_over", "rid":self.rid, "player_stat":player_stat, "winner":winner, "admin":admin}}
            db.save_game_over(self.rid, 0, "room_over", uid, json_encode(pub_data))
            db.commit()
            nsq_pub(pub_data)
            
        db.dismiss_room(self.rid)
        db.commit()
            
        for member in self.members:
            member_player = Player(member, self.bid, self.rid)
            member_player.clear()
        self.clear_room()
    
    
    